/* Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved.
 * Description: gmem test comlib
 * Author: fengyao19@huawei.com
 * Create: 2024-11-06
 */
#ifndef __gmem_test_comlib_H__
#define __gmem_test_comlib_H__

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <stdbool.h>
#include <signal.h>
#include <errno.h>
#include </usr/local/Ascend/ascend-toolkit/latest/aarch64-linux/include/acl/acl.h>
#include <asm-generic/mman-common.h>
#include "rpg_dev.h"
// void wait_signal(void);
// bool check_data(void *addr, long long int len, int exp_w);
// //int get_gmem_fd(void);
// int get_hnid(int device_id);
// bool init_dev(int device_id);
// //int init_dev_get_hnid(int device_id);
// bool set_dev(int device_id);
// bool reset_dev(int device_id);
// void *gmem_mmap(void *addr, long long int size, int port, int flags, long long int m_size, int m_words);
// //int check_ioctl_ret(int fd, struct hmadvise_arg *arg, int exp_res);
// int check_ioctl_gmem_memcpy_ret(int fd, struct hmemcpy_arg *arg, int exp_res);

/**
 * @brief 初始化+进程绑定设备
 *
 * @param device_id 设备id(npu-smi info查到的设备序号)
 * @return true
 * @return false
 */
bool init_dev(int device_id)
{
    if (aclInit(NULL) != ACL_SUCCESS) {
        printf("aclInit FAILED\n");
        return false;
    }
    if (aclrtSetDevice(device_id) != ACL_SUCCESS) {
        printf("aclrtSetDevice FAILED\n");
        return false;
    }
    return true;
}

/**
 * @brief 获取/dev/gmem 文件句柄gmem_fd
 *
 * @return int gmem_fd
 */
int get_gmem_fd(void)
{
    int gmem_fd = open("/dev/gmem", O_RDWR);
    if (gmem_fd == -1) {
        perror("Error opening file /dev/gmem");
        printf("opening file /dev/gmem FAILED\n");
        return -1;
    }
    return gmem_fd;
}

/**
 * @brief 获取设备hnid
 *
 * @param device_id device_id: 设备id(npu-smi info查到的设备序号)
 * @return int 设备hnid
 */
int get_hnid(int device_id)
{
    int id;
    struct gmem_hnid_arg arg;
    arg.hnuma_id = &id;

    int gmem_fd = get_gmem_fd();
    if (gmem_fd == -1) {
        return -1;
    }
    if (ioctl(gmem_fd, GMEM_GET_HNUMA_ID, &arg) < 0) {
        perror("ioctl GMEM_GET_HNUMA_ID");
        printf("ioctl GMEM_GET_HNUMA_ID FAILED");
        return -1;
    }
    printf("get_hnid: %d\n", *arg.hnuma_id);
    return *arg.hnuma_id;
}

/**
 * @brief 检查GMEM_MADVISE(MADV_PREFETCH、MADV_DONTNEED) gmem页面预取、gmem页面拷贝ioctl调用返回值
 *
 * @param fd gmem_fd(/dev/gmem文件句柄)
 * @param arg hmadvise_arg
 * @param exp_res 预期ioctl调用返回值
 * @return int 0:与预期相符 -1:与预期不符
 */
int check_ioctl_ret(int fd, struct hmadvise_arg *arg, int exp_res)
{
    int ret = 0;
    printf("in check_ioctl_ret, params: gmem_fd:%d, start_addr:%p, len_in:%ld, hnid:%d, exp_res=%d\n",
        fd,
        (void *)arg->start,
        arg->len_in,
        arg->hnid,
        exp_res);
    //if (arg->behavior == MADV_PREFETCH) {
    //    system("echo 'before_prefetch'; cat /proc/meminfo | grep -i AnonHugePages; npu-smi info");
    //} else {
    //    system("echo 'before_dontneed'; cat /proc/meminfo | grep -i AnonHugePages; npu-smi info");
    //}

    // 调用ioctl进行页面预取/页面废弃
    int res = ioctl(fd, GMEM_MADVISE, arg);
    if (res == exp_res) {
        perror("ioctl GMEM_MADVISE");
        printf("ioctl GMEM_MADVISE check return SUCCESS;res:%d, errno:%d, errmsg:%s;\n", res, errno, strerror(errno));
    } else {
        printf("ioctl GMEM_MADVISE res incorrect, res:%d, errno:%d, errmsg:%s; FAILED\n",
            res,
            errno,
            strerror(errno));
        ret = -1;
    }
    //if (arg->behavior == MADV_PREFETCH) {
    //    system("echo 'after_prefetch'; cat /proc/meminfo | grep -i AnonHugePages; npu-smi info");
    //} else {
    //    system("echo 'after_dontneed'; cat /proc/meminfo | grep -i AnonHugePages; npu-smi info");
    //}
    return ret;
}


void gmem_prefetch(void *addr, long long int size, int device_id) {
    int ret;
    struct hmadvise_arg hm_arg;
    int hnid = get_hnid(device_id);
    int gmem_fd = open("/dev/gmem", O_RDWR);
    if (gmem_fd == -1) {
        perror("Error opening file /dev/gmem");
        return;
    }
    hm_arg.start = (unsigned long)addr;
    hm_arg.len_in = (size_t)size;
    hm_arg.behavior = MADV_PREFETCH;
    hm_arg.hnid = hnid;
    ret = check_ioctl_ret(gmem_fd, &hm_arg, 0);
    close(gmem_fd);
    return;
}

#endif
